﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace MrBrooks.Utility
{
    /// <summary>
    /// 流
    /// </summary>
    public sealed class BinaryHelper
    {
        /// <summary>
        /// 缓存
        /// </summary>
        private const int bufferSize = 1024;

        /// <summary>
        /// 数据备份
        /// </summary>
        /// <param name="source"></param>
        /// <param name="path"></param>
        public static void Backup(object source, string path)
        {
            try
            {
                if (source == null)
                {
                    if (File.Exists(path)) File.Delete(path);
                    return;
                }
                Stream stream = new MemoryStream();
                var bfm = new BinaryFormatter();
                bfm.Serialize(stream, source);
                stream.Seek(0, SeekOrigin.Begin);

                using (var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
                {
                    int read = 0;
                    while (read < stream.Length)
                    {
                        byte[] wd;
                        if (stream.Length - read < bufferSize)
                            wd = new byte[stream.Length - read];
                        else
                            wd = new byte[bufferSize];
                        stream.Read(wd, 0, wd.Length);
                        fs.Write(wd, 0, wd.Length);
                        read += wd.Length;
                    }
                    fs.Flush();
                }
            }
            catch { }
        }

        /// <summary>
        /// 数据还原
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static T Restore<T>(string path)
        {
            if (File.Exists(path))
            {
                try
                {
                    FileStream fs = File.OpenRead(path);
                    byte[] b = new byte[fs.Length];
                    fs.Read(b, 0, b.Length);
                    fs.Close();

                    BinaryFormatter transfer = new BinaryFormatter();
                    MemoryStream ms = new MemoryStream(b);
                    T target = (T)transfer.Deserialize(ms);
                    ms.Close();
                    return target;
                }
                catch { }
            }
            return default(T);
        }

        /// <summary>
        /// 将对像序列化为字符串
        /// (类似JSON, 但不是很严格, 只能用于测试)
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string SerializeToString(object obj)
        {
            var sb = new StringBuilder();
            SerializeValue(obj, sb, 0);
            return sb.ToString();
        }

        #region .Privates.
        private static void SerializeValue(object o, StringBuilder sb, int depth)
        {
            ++depth;
            if ((o == null) || DBNull.Value.Equals(o))
            {
                sb.Append("null");
            }
            else
            {
                string input = o as string;
                if (input != null)
                {
                    sb.AppendFormat(@"""{0}""", o);
                }
                else if (o is char)
                {
                    if (((char)o) == '\0')
                    {
                        sb.Append("null");
                    }
                    else
                    {
                        sb.AppendFormat(@"""{0}""", o);
                    }
                }
                else if (o is bool)
                {
                    var b = (bool)o;
                    sb.Append(b ? "true" : "false");
                }
                else if (o is DateTime)
                {
                    sb.AppendFormat(@"""{0}""", ((DateTime)o).ToString("yyyy-MM-dd HH:mm:ss"));
                }
                else if (o is DateTimeOffset)
                {
                    var offset = (DateTimeOffset)o;
                    sb.Append("\"\\/Date(");
                    sb.Append((long)((offset.UtcDateTime.ToUniversalTime().Ticks - 0) / 0x2710));
                    sb.Append(")\\/\"");

                }
                else if (o is Guid)
                {
                    sb.AppendFormat(@"""{0}""", (Guid)o);
                }
                else
                {
                    Uri uri = o as Uri;
                    if (uri != null)
                    {
                        sb.AppendFormat(@"""{0}""", uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped));
                    }
                    else if (o is double)
                    {
                        sb.Append(((double)o).ToString("r", CultureInfo.InvariantCulture));
                    }
                    else if (o is float)
                    {
                        sb.Append(((float)o).ToString("r", CultureInfo.InvariantCulture));
                    }
                    else if (o.GetType().IsPrimitive || (o is decimal))
                    {
                        IConvertible convertible = o as IConvertible;
                        if (convertible != null)
                        {
                            sb.Append(convertible.ToString(CultureInfo.InvariantCulture));
                        }
                        else
                        {
                            sb.Append(o.ToString());
                        }
                    }
                    else
                    {
                        var enumType = o.GetType();
                        if (enumType.IsEnum)
                        {
                            sb.Append(((Enum)o).ToString("D"));
                        }
                        else
                        {
                            try
                            {
                                IDictionary dictionary = o as IDictionary;
                                if (dictionary != null)
                                {
                                    SerializeDictionary(dictionary, sb, depth);
                                }
                                else
                                {
                                    IEnumerable enumerable = o as IEnumerable;
                                    if (enumerable != null)
                                    {
                                        SerializeEnumerable(enumerable, sb, depth);
                                    }
                                    else
                                    {
                                        SerializeCustomObject(o, sb, depth);
                                    }
                                }
                            }
                            catch
                            {
                                sb.Append("n-u-l-l");
                            }
                        }
                    }
                }
            }

        }

        private static void SerializeDictionaryKeyValue(string key, object value, StringBuilder sb, int depth)
        {
            SerializeString(key, sb);
            sb.Append(':');
            SerializeValue(value, sb, depth);
        }

        private static void SerializeString(string input, StringBuilder sb)
        {
            sb.AppendFormat(@"""{0}""", input);
        }

        private static void SerializeDictionary(IDictionary o, StringBuilder sb, int depth)
        {
            sb.Append('{');
            bool flag = true;
            foreach (DictionaryEntry entry in o)
            {
                string key = entry.Key.ToString();
                if (!flag)
                {
                    sb.Append(',');
                }
                SerializeDictionaryKeyValue(key, entry.Value, sb, depth);
                flag = false;
            }
            sb.Append('}');
        }

        private static void SerializeEnumerable(IEnumerable enumerable, StringBuilder sb, int depth)
        {
            sb.Append('[');
            bool flag = true;
            foreach (object obj2 in enumerable)
            {
                if (!flag)
                {
                    sb.Append(',');
                }
                SerializeValue(obj2, sb, depth);
                flag = false;
            }
            sb.Append(']');
        }

        private static void SerializeCustomObject(object o, StringBuilder sb, int depth)
        {
            bool flag = true;
            Type type = o.GetType();
            sb.Append('{');
            foreach (FieldInfo info in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
            {
                if (!flag)
                {
                    sb.Append(',');
                }
                SerializeString(info.Name, sb);
                sb.Append(':');
                SerializeValue(FieldInfoGetValue(info, o), sb, depth);
                flag = false;
            }
            foreach (PropertyInfo info2 in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
            {
                MethodInfo getMethod = info2.GetGetMethod();
                if ((getMethod != null) && (getMethod.GetParameters().Length <= 0))
                {
                    if (!flag)
                    {
                        sb.Append(',');
                    }
                    SerializeString(info2.Name, sb);
                    sb.Append(':');
                    SerializeValue(MethodInfoInvoke(getMethod, o, null), sb, depth);
                    flag = false;
                }
            }
            sb.Append('}');
        }

        private static object FieldInfoGetValue(FieldInfo field, object target)
        {
            Type declaringType = field.DeclaringType;
            if (declaringType == null)
            {
                if (!field.IsPublic)
                {
                    DemandGrantSet(field.Module.Assembly);
                }
            }
            else if ((!declaringType.IsVisible) || !field.IsPublic)
            {
                DemandReflectionAccess(declaringType);
            }
            return field.GetValue(target);
        }


        private static void DemandReflectionAccess(Type type)
        {
            try
            {
                MemberAccessPermission.Demand();
            }
            catch (SecurityException)
            {
                DemandGrantSet(type.Assembly);
            }
        }


        private static ReflectionPermission memberAccessPermission;
        private static ReflectionPermission MemberAccessPermission
        {
            get
            {
                if (memberAccessPermission == null)
                {
                    memberAccessPermission = new ReflectionPermission(ReflectionPermissionFlag.MemberAccess);
                }
                return memberAccessPermission;
            }
        }

        [SecuritySafeCritical]
        private static void DemandGrantSet(Assembly assembly)
        {
            PermissionSet permissionSet = assembly.PermissionSet;
            permissionSet.AddPermission(RestrictedMemberAccessPermission);
            permissionSet.Demand();
        }

        private static ReflectionPermission restrictedMemberAccessPermission;
        private static ReflectionPermission RestrictedMemberAccessPermission
        {
            get
            {
                if (restrictedMemberAccessPermission == null)
                {
                    restrictedMemberAccessPermission = new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess);
                }
                return restrictedMemberAccessPermission;
            }
        }

        private static object MethodInfoInvoke(MethodInfo method, object target, object[] args)
        {
            Type declaringType = method.DeclaringType;
            if (declaringType == null)
            {
                if (!method.IsPublic || !GenericArgumentsAreVisible(method))
                {
                    DemandGrantSet(method.Module.Assembly);
                }
            }
            else if ((!declaringType.IsVisible || !method.IsPublic) || !GenericArgumentsAreVisible(method))
            {
                DemandReflectionAccess(declaringType);
            }
            return method.Invoke(target, args);
        }


        private static bool GenericArgumentsAreVisible(MethodInfo method)
        {
            if (method.IsGenericMethod)
            {
                foreach (Type type in method.GetGenericArguments())
                {
                    if (!type.IsVisible)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
        #endregion
    }
}
